package pres.lnk.utils;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.security.KeyStore;
import java.util.Objects;

/**
 * https指定证书访问
 *
 * @author linnankun
 * @date 2022/4/25
 */
public class HttpsCertificateAuthInitializer implements RestfulHttpClient.URLConnectionInitializer {
    private final static Logger logger = LoggerFactory.getLogger(HttpsCertificateAuthInitializer.class);

    public static final String PASSWORD = "changeit";
    public static final String TLS = "TLS";
    public static final String TLS1 = "TLSv1";
    public static final String TLS1_1 = "TLSv1.1";
    public static final String TLS1_2 = "TLSv1.2";
    public static final String SSL = "SSL";
    public static final String SSL2 = "SSLv2";
    public static final String SSL3 = "SSLv3";


    private final File certificateFile;
    private final byte[] certificateData;
    private String password = PASSWORD;
    private String algorithm = TLS;

    public HttpsCertificateAuthInitializer(File certificateFile) throws FileNotFoundException {
        this.certificateFile = Objects.requireNonNull(certificateFile);
        this.certificateData = null;

        if (!certificateFile.exists()) {
            throw new FileNotFoundException(certificateFile.getAbsolutePath());
        }
    }

    public HttpsCertificateAuthInitializer(byte[] certificateData) {
        this.certificateData = Objects.requireNonNull(certificateData);
        this.certificateFile = null;
    }

    public HttpsCertificateAuthInitializer(String certificateFilepath) throws FileNotFoundException {
        this(new File(certificateFilepath));
    }

    public HttpsCertificateAuthInitializer(InputStream certificateInputStream) throws IOException {
        this(IOUtils.toByteArray(certificateInputStream));
    }


    @Override
    public HttpURLConnection init(HttpURLConnection connection, RestfulHttpClient.HttpClient client) {
        String protocol = connection.getURL().getProtocol();
        if ("https".equalsIgnoreCase(protocol)) {
            HttpsURLConnection https = (HttpsURLConnection) connection;
            try {
                loadCertificate(https);
            } catch (Exception e) {
                throw new RuntimeException("load https certificate failed", e);
            }
            return https;
        }
        return connection;
    }

    private void loadCertificate(HttpsURLConnection connection) throws Exception {
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        SSLContext sslContext = SSLContext.getInstance(algorithm);

        try (InputStream input = getInputStream()) {
            ks.load(input, password.toCharArray());
        }

        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);

        sslContext.init(null, tmf.getTrustManagers(), null);
        SSLSocketFactory newFactory = sslContext.getSocketFactory();
        connection.setSSLSocketFactory(newFactory);
    }

    public static void main(String[] args) throws IOException {
        String url = "https://b2b.10086.cn/b2b/main/viewNoticeContent.html?noticeBean.id=";

        final HttpsCertificateAuthInitializer httpsAutoAuthInitializer = new HttpsCertificateAuthInitializer(
                "D:\\b2b.10086.cn.cer");
        RestfulHttpClient.HttpResponse response = RestfulHttpClient.getClient(url)
                .addInitializer(httpsAutoAuthInitializer)
                .request(); //发起请求

        System.out.println(response.getCode());     //响应状态码
        System.out.println(response.getRequestUrl());//最终发起请求的地址
        if (response.getCode() == 200) {
            //请求成功
            System.out.println(response.getContent());  //响应内容
        }
    }

    private InputStream getInputStream() throws FileNotFoundException {
        if (certificateFile != null) {
            return new FileInputStream(certificateFile);
        } else if (certificateData != null) {
            return new ByteArrayInputStream(certificateData);
        }
        throw new RuntimeException("未设置https证书");
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getAlgorithm() {
        return algorithm;
    }

    public void setAlgorithm(String algorithm) {
        this.algorithm = algorithm;
    }

}
